/*
# Copyright (c) 2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/
#include <pjmedia-audiodev/audiodev_imp.h>
#include <pj/assert.h>
#include <pj/atomic_queue.h>
#include <pj/lock.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pjmedia/circbuf.h>
#include <pjmedia/errno.h>
#include <pjmedia/event.h>


#if defined(PJMEDIA_AUDIO_DEV_HAS_OHOS) &&  PJMEDIA_AUDIO_DEV_HAS_OHOS != 0

#include "audio_device_info.h"
#include <ohaudio/native_audiostream_base.h>
#include <ohaudio/native_audiostreambuilder.h>
#include <ohaudio/native_audiocapturer.h>
#include <hilog/log.h>

#define THIS_FILE       "ohos_dev.c"
#define DRIVER_NAME     "OHOS_AUDO_DEV"

/* Device info */
typedef struct aud_dev_info
{
    pjmedia_aud_dev_info         info;          /**< Base info          */
    int                          id;            /**< Original dev ID    */
} aud_dev_info;

/* ohos factory */
struct ohos_aud_factory
{
    pjmedia_aud_dev_factory      base;
    pj_pool_factory             *pf;
    pj_pool_t                   *pool;

    pj_pool_t                   *dev_pool;      /**< Device list pool  */
    unsigned                     dev_count;     /**< Device count      */
    aud_dev_info                *dev_info;      /**< Device info list  */
};

class AudioRenderer;
class AudioCapturer;

struct ohos_aud_stream
{
    pjmedia_aud_stream  base;
    pj_pool_t          *pool;
    pj_str_t            name;
    pjmedia_dir         dir;
    pjmedia_aud_param   param;
    struct ohos_aud_factory *f;

    int                 bytes_per_sample;
    pj_uint32_t         samples_per_sec;
    unsigned            samples_per_frame;
    int                 channel_count;
    void               *user_data;
    pj_bool_t           running;

    /* Capture/record */
    AudioCapturer       *rec_engine;
    pjmedia_aud_rec_cb  rec_cb;

    /* Playback */
    AudioRenderer       *play_engine;
    pjmedia_aud_play_cb play_cb;
};

/* Factory prototypes */
static pj_status_t ohos_audo_dev_init(pjmedia_aud_dev_factory *f);
static pj_status_t ohos_audo_dev_destroy(pjmedia_aud_dev_factory *f);
static pj_status_t ohos_audo_dev_refresh(pjmedia_aud_dev_factory *f);
static unsigned ohos_audo_dev_get_dev_count(pjmedia_aud_dev_factory *f);
static pj_status_t ohos_audo_dev_get_dev_info(pjmedia_aud_dev_factory *f,
                                        unsigned index,
                                        pjmedia_aud_dev_info *info);
static pj_status_t ohos_audo_dev_default_param(pjmedia_aud_dev_factory *f,
                                         unsigned index,
                                         pjmedia_aud_param *param);
static pj_status_t ohos_audo_dev_create_stream(pjmedia_aud_dev_factory *f,
                                         const pjmedia_aud_param *param,
                                         pjmedia_aud_rec_cb rec_cb,
                                         pjmedia_aud_play_cb play_cb,
                                         void *user_data,
                                         pjmedia_aud_stream **p_aud_strm);

/* Stream prototypes */
static pj_status_t strm_get_param(pjmedia_aud_stream *strm,
                                  pjmedia_aud_param *param);
static pj_status_t strm_get_cap(pjmedia_aud_stream *strm,
                                pjmedia_aud_dev_cap cap,
                                void *value);
static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
                                pjmedia_aud_dev_cap cap,
                                const void *value);
static pj_status_t strm_start(pjmedia_aud_stream *strm);
static pj_status_t strm_stop(pjmedia_aud_stream *strm);
static pj_status_t strm_destroy(pjmedia_aud_stream *strm);


static pjmedia_aud_dev_factory_op ohos_audo_dev_op =
{
    &ohos_audo_dev_init,
    &ohos_audo_dev_destroy,
    &ohos_audo_dev_get_dev_count,
    &ohos_audo_dev_get_dev_info,
    &ohos_audo_dev_default_param,
    &ohos_audo_dev_create_stream,
    &ohos_audo_dev_refresh
};

static pjmedia_aud_stream_op ohos_audo_dev_strm_op =
{
    &strm_get_param,
    &strm_get_cap,
    &strm_set_cap,
    &strm_start,
    &strm_stop,
    &strm_destroy
};

/*
 * Init ohos audio driver.
 */
#ifdef __cplusplus
extern "C"{
#endif
pjmedia_aud_dev_factory* pjmedia_ohos_factory(pj_pool_factory *pf)
{
    struct ohos_aud_factory *f;
    pj_pool_t *pool;
    PJ_LOG(4, (THIS_FILE, "ohos_avdio pjmedia_ohos_factory"));  
    pool = pj_pool_create(pf, "ohos_audio", 256, 256, NULL);
    f = PJ_POOL_ZALLOC_T(pool, struct ohos_aud_factory);
    f->pf = pf;
    f->pool = pool;
    f->base.op = &ohos_audo_dev_op;
    f->dev_pool = pj_pool_create(pf, "ohos_dev", 256, 256, NULL);

    return &f->base;
}
#ifdef __cplusplus
}
#endif

/**ohos auido play*/
class AudioRenderer {
private:
    struct ohos_aud_stream *stream;
    uint32_t sampleRate;
    uint32_t channelCount;
    uint32_t frameSize;
    OH_AudioRenderer *renderer_{nullptr};
    OH_AudioStreamBuilder *builder{nullptr};
    pj_bool_t thread_registered	= PJ_FALSE;
    pj_thread_desc desc;
    pj_thread_t	*pthread;

public:
    AudioRenderer(struct ohos_aud_stream *stream_, uint32_t rate, uint32_t channel_count, uint32_t frame_size)
        : stream(stream_), sampleRate(rate), channelCount(channel_count),  frameSize(frame_size) {
        initAudioRenderer();
    }
    ~AudioRenderer() { 
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "ohos_dev", "AudioRenderer ohos_dev ~AudioRenderer");
        destroy(); 
    }
    static int32_t OnWriteData_1(OH_AudioRenderer *renderer, void *userData, void *buffer, int32_t length) {
        if (userData == NULL) {
            return 0;
        }
        return static_cast<AudioRenderer *>(userData)->OnWriteData(renderer, buffer, length);
    }

    static int32_t OnStreamEvent_1(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Event event) {
        if (userData == NULL) {
            return 0;
        }
        return static_cast<AudioRenderer *>(userData)->OnStreamEvent(renderer, event);
    }

    static int32_t OnInterruptEvent_1(OH_AudioRenderer *renderer, void *userData, OH_AudioInterrupt_ForceType type,
                                      OH_AudioInterrupt_Hint hint) {
        if (userData == NULL) {
            return 0;
        }
        return static_cast<AudioRenderer *>(userData)->OnInterruptEvent(renderer, type, hint);
    }

    static int32_t OnError_1(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Result error) {
        if (userData == NULL) {
            return 0;
        }
        return static_cast<AudioRenderer *>(userData)->OnError(renderer, error);
    }

    // 函数用于将void *数组转换为int16_t数组
    void convertVoidToInt16(void *buffer, int bufferSize, int16_t **result, size_t *resultSize) {
        *resultSize = bufferSize / sizeof(int16_t);
        *result = (int16_t *)malloc(*resultSize * sizeof(int16_t));
        if (*result == NULL) {
            return;
        }
        // 使用memcpy进行内存复制
        memcpy(*result, buffer, bufferSize);
    }
    int32_t OnWriteData(OH_AudioRenderer *renderer, void *buffer, int32_t length) {
        (void)renderer;
        int64_t position;
        int64_t timestamp;

        if(!thread_registered && !pj_thread_is_registered())
        {
            if (pj_thread_register(NULL,desc,&pthread) == PJ_SUCCESS)
            {
                thread_registered = PJ_TRUE;
            }
        }
        auto result = OH_AudioRenderer_GetTimestamp(renderer_, CLOCK_MONOTONIC, &position, &timestamp);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Ohos stream GetTimestamp fail"));
            return -1;
        }
    
        // int16_t *resultBuffer;
        // size_t resultSize;
        // convertVoidToInt16(buffer,length,&resultBuffer,&resultSize);
        pjmedia_frame frame;
        frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
        frame.size = length;
        frame.bit_info = 16;
        frame.buf = std::move(buffer);
        frame.timestamp.u64 = timestamp;
        pj_status_t status = (*stream->play_cb)(stream->user_data, &frame);
        if (status != PJ_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Ohos stream play_cb callback fail"));
         }
        // free(resultBuffer);
        return 0;
    }

    int32_t OnStreamEvent(OH_AudioRenderer *renderer, OH_AudioStream_Event event) {
        (void)renderer;
        return 0;
    }

    int32_t OnInterruptEvent(OH_AudioRenderer *renderer, OH_AudioInterrupt_ForceType type,
                             OH_AudioInterrupt_Hint hint) {
        (void)renderer;
        (void)hint;

        return 0;
    }

    int32_t OnError(OH_AudioRenderer *renderer, OH_AudioStream_Result error) {
        (void)renderer;
        PJ_LOG(3, (THIS_FILE, "AudioRenderer Ohos stream callback error:%d", error));
        return 0;
    }

   int start() {
        OH_AudioStream_Result ret = OH_AudioStream_Result::AUDIOSTREAM_ERROR_SYSTEM;
        if (renderer_) {
            ret= OH_AudioRenderer_Start(renderer_);
        }else {
            initAudioRenderer();
            ret= OH_AudioRenderer_Start(renderer_);
        }
        return ret;
    }

    void stop() { OH_AudioRenderer_Stop(renderer_); }

    void destroy() {
        if (renderer_) {
            OH_AudioRenderer_Release(renderer_);
            renderer_ = nullptr;
        }
        if (builder)
        {
            OH_AudioStreamBuilder_Destroy(builder);
            builder = nullptr;
        }
        
    }

private:
    void initAudioRenderer() {
        PJ_LOG(4, (THIS_FILE, "initAudioRenderer start"));
        OH_AudioStreamBuilder *builder = nullptr;
        auto result = OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Creating Ohos stream Builder fail"));
            return;
        }
        result = OH_AudioStreamBuilder_SetSamplingRate(builder, sampleRate);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Creating Ohos stream SetSamplingRate fail"));
            return;
        }
        result = OH_AudioStreamBuilder_SetChannelCount(builder, channelCount);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Creating Ohos stream SetChannelCount fail"));
            return;
        }
        result = OH_AudioStreamBuilder_SetLatencyMode(builder, AUDIOSTREAM_LATENCY_MODE_FAST);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Creating Ohos stream SetLatencyMode fail"));
            return;
        }
        OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
        OH_AudioRenderer_Callbacks callbacks;
        callbacks.OH_AudioRenderer_OnWriteData = OnWriteData_1;
        callbacks.OH_AudioRenderer_OnStreamEvent = OnStreamEvent_1;
        callbacks.OH_AudioRenderer_OnInterruptEvent = OnInterruptEvent_1;
        callbacks.OH_AudioRenderer_OnError = OnError_1;
        result = OH_AudioStreamBuilder_SetRendererCallback(builder, callbacks, this);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Creating Ohos stream SetRendererCallback fail"));
            return;
        }
        OH_AudioRenderer *stream = nullptr;
        OH_AudioStreamBuilder_SetFrameSizeInCallback(builder,frameSize/2);
        result = OH_AudioStreamBuilder_GenerateRenderer(builder, &stream);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioRenderer Creating Ohos stream GenerateRenderer fail"));
            return;
        }
        renderer_ = stream;
        PJ_LOG(4, (THIS_FILE, "initAudioRenderer success"));
    }
};
/**ohos auido capture*/
class AudioCapturer {
public:
    struct ohos_aud_stream *stream;
    uint32_t sampleRate;
    uint32_t channelCount;
    OH_AudioCapturer *audioCapturer{nullptr};
    OH_AudioStreamBuilder *builder {nullptr};
    pj_bool_t thread_registered	= PJ_FALSE;
    pj_thread_desc desc;
    pj_thread_t	*pthread;
public:
    AudioCapturer(struct ohos_aud_stream *stream_, uint32_t rate, uint32_t count)
        : stream(stream_), sampleRate(stream_->param.clock_rate), channelCount(count) {
        initAudioCapt();
    }
    ~AudioCapturer() { 
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "ohos_dev", "OH_AudioCapturer ohos_dev ~AudioCapturer");
        destroy(); }
    int start() {
        OH_AudioStream_Result ret = OH_AudioStream_Result::AUDIOSTREAM_ERROR_SYSTEM;
        if (audioCapturer) {
            ret = OH_AudioCapturer_Start(audioCapturer);
        }else {
            initAudioCapt();
            ret = OH_AudioCapturer_Start(audioCapturer);
        }
        return ret;
    }

    void stop() {
        if (audioCapturer) {
            OH_AudioCapturer_Stop(audioCapturer);
        }
    }

    void destroy() {
        PJ_LOG(3, (THIS_FILE, "AudioCapturer ------destroy"));
        if (audioCapturer) {
            OH_AudioCapturer_Release(audioCapturer);
            audioCapturer = nullptr;
        }
        if (builder)
        {
           OH_AudioStreamBuilder_Destroy(builder);
           builder = nullptr;
        }
        
    }

    static int32_t OnReadData_1(OH_AudioCapturer *stream, void *userData, void *buffer, int32_t length) {
        AudioCapturer *self = reinterpret_cast<AudioCapturer *>(userData);
        return self->OnReadData(stream, buffer, length);
    }

    static int32_t Capt_OnError_1(OH_AudioCapturer *stream, void *userData, OH_AudioStream_Result error) {
        AudioCapturer *self = reinterpret_cast<AudioCapturer *>(userData);
        return self->OnError(stream, error);
    }

    int32_t OnError(OH_AudioCapturer *stream, OH_AudioStream_Result error) {
        (void)stream;
        PJ_LOG(3, (THIS_FILE, "AudioCapturer Ohos stream callback error:%d", error));
        return 0;
    }

       // 函数用于将void *数组转换为int16_t数组
    void convertVoidToInt16(void *buffer, int bufferSize, int16_t **result, size_t *resultSize) {
        *resultSize = bufferSize / sizeof(int16_t);
        *result = (int16_t *)malloc(*resultSize * sizeof(int16_t));
        if (*result == NULL) {
            return;
        }
        // 使用memcpy进行内存复制
        memcpy(*result, buffer, bufferSize);
    }

    int32_t OnReadData(OH_AudioCapturer *renderer, void *buffer, int32_t length) {
        (void)renderer;
        int64_t position;
        int64_t timestamp;
        if(!thread_registered && !pj_thread_is_registered())
        {
            if (pj_thread_register(NULL,desc,&pthread) == PJ_SUCCESS)
            {
                thread_registered = PJ_TRUE;
            }
        }
        auto result = OH_AudioCapturer_GetTimestamp(audioCapturer, CLOCK_MONOTONIC, &position, &timestamp);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioCapturer Ohos stream GetTimestamp fail"));
            return -1;
        }
        
        pjmedia_frame frame;
        frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
        frame.size = length;
        frame.bit_info = 16;
        frame.buf = std::move(buffer);
        frame.timestamp.u64 = timestamp;
        pj_status_t status = (*stream->rec_cb)(stream->user_data, &frame);
        if (status != PJ_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioCapturer Ohos stream rec_cb callback fail"));
        }
        // free(resultBuffer);
        return 0;
    }

private:
    void initAudioCapt() {
        PJ_LOG(4, (THIS_FILE, "initAudioCapt start,audioCapturer:%d",audioCapturer));
        if (audioCapturer) {
            destroy();
        }
       
        auto result = OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_CAPTURER);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioCapturer Creating Ohos stream Builder fail"));
            return;
        }
        result = OH_AudioStreamBuilder_SetSamplingRate(builder, sampleRate);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioCapturer Creating Ohos stream SetSamplingRate fail"));
            return;
        }
        result = OH_AudioStreamBuilder_SetChannelCount(builder, channelCount);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioCapturer Creating Ohos stream SetChannelCount fail"));
            return;
        }
        result = OH_AudioStreamBuilder_SetLatencyMode(builder, AUDIOSTREAM_LATENCY_MODE_FAST);
        if (result != AUDIOSTREAM_SUCCESS) {
            PJ_LOG(3, (THIS_FILE, "AudioCapturer Creating Ohos stream SetLatencyMode fail"));
            return;
        }
        OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
        OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_RAW);
        OH_AudioCapturer_Callbacks callbacks;
        memset(&callbacks, 0, sizeof(OH_AudioCapturer_Callbacks));
        callbacks.OH_AudioCapturer_OnReadData = OnReadData_1;
        callbacks.OH_AudioCapturer_OnError = Capt_OnError_1;
        callbacks.OH_AudioCapturer_OnInterruptEvent = nullptr;
        callbacks.OH_AudioCapturer_OnStreamEvent = nullptr;
        OH_AudioStreamBuilder_SetCapturerCallback(builder, callbacks, this);
        OH_AudioCapturer *stream = nullptr;
        OH_AudioStreamBuilder_GenerateCapturer(builder, &stream);
        audioCapturer = stream;
        PJ_LOG(4, (THIS_FILE, "initAudioCapt success"));
    }
};
/* API: Init factory */
static pj_status_t ohos_audo_dev_init(pjmedia_aud_dev_factory *f)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_init"));
    pj_status_t status;
    status = ohos_audo_dev_refresh(f);
    if (status != PJ_SUCCESS)
        return status;

    return PJ_SUCCESS;
}

/* API: refresh the list of devices */
static pj_status_t ohos_audo_dev_refresh(pjmedia_aud_dev_factory *ff)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_refresh"));
    struct ohos_aud_factory *f = (struct ohos_aud_factory*)ff;
     pj_status_t status = PJ_SUCCESS;
     int i, dev_count = 0;
     auto devices= AudioDeviceManager::GetDevices();
     f->dev_count = 0;
     pj_pool_reset(f->dev_pool);
     dev_count = devices.size();
     if (dev_count < 0)
     {
        PJ_LOG(3, (THIS_FILE, "Failed to get device count from ohos"));
        status = PJMEDIA_EAUD_SYSERR;
     }
    f->dev_info = (aud_dev_info*)
                  pj_pool_calloc(f->dev_pool, dev_count,
                                 sizeof(aud_dev_info));
    if (f->dev_info == NULL) {
        PJ_LOG(4, (THIS_FILE, "ohos audio->f->dev_info is null"));     
    }

    for (i = 0; i < dev_count; i++) {   
        aud_dev_info *adi = &f->dev_info[i];
        pjmedia_aud_dev_info *base_adi = &adi->info;
        auto classDeviceInfo = devices[i];
        adi->id = classDeviceInfo.id;
        uint32_t size  = classDeviceInfo.size;
        PJ_LOG(4, (THIS_FILE, "ohos classDeviceInfo ----- >outputCount:%d,intputCount:%d",classDeviceInfo.outputCount,classDeviceInfo.inputCount));     
        for(int j = 0; j < size; j++){
            //需要区分是输入设备还是输出设备
            if (classDeviceInfo.outputCount > 0){
                //输出设备
                base_adi->input_count = 0;
                base_adi->output_count = classDeviceInfo.supportedChannelCounts[j];
                base_adi->default_samples_per_sec = classDeviceInfo.supportedClockRates[j];
            } else {
               //输入设备
                base_adi->output_count = 0;
                base_adi->input_count = classDeviceInfo.supportedChannelCounts[j];
                base_adi->default_samples_per_sec = classDeviceInfo.supportedClockRates[j];
            }
            base_adi->caps = 0;
            const char *strtmp = classDeviceInfo.name.c_str();
            pj_ansi_strxcpy(base_adi->name, strtmp, sizeof(base_adi->name));
            pj_ansi_strxcpy(base_adi->driver, DRIVER_NAME,
                        sizeof(base_adi->driver));
            f->dev_count++;
            PJ_LOG(4, (THIS_FILE, "ohos audio-> classDeviceInfo supportedClockRates:%d", classDeviceInfo.supportedClockRates));
            PJ_LOG(4, (THIS_FILE, "ohos audio-> adi->id:%d", adi->id));
            PJ_LOG(4, (THIS_FILE, "ohos audio->supportedClockRates:%d",base_adi->default_samples_per_sec));      
        }          
    }
    PJ_LOG(4, (THIS_FILE,
               "ohos audio device initialized with %d device(s):",
               f->dev_count));
    for (i = 0; i < f->dev_count; i++) {
        aud_dev_info *adi = &f->dev_info[i];
        PJ_LOG(4, (THIS_FILE, "%2d (native id=%d): %s (%s%s%s)",
                   i, adi->id, adi->info.name,
                   adi->info.input_count?"in":"",
                   adi->info.input_count && adi->info.output_count?"+":"",
                   adi->info.output_count?"out":""
                ));  
         PJ_LOG(4, (THIS_FILE, "aud_dev_info  samples_per:%d",adi->info.default_samples_per_sec));
         PJ_LOG(4, (THIS_FILE, "aud_dev_info  id:%d",adi->id));             
    }                                     
    return status;
}

static pj_status_t ohos_audo_dev_destroy(pjmedia_aud_dev_factory *f)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_destroy"));
    struct ohos_aud_factory *pa = (struct ohos_aud_factory*)f;
    pj_pool_t *pool;

    PJ_LOG(4, (THIS_FILE, "ohos sound library shutting down.."));

    pool = pa->pool;
    pa->pool = NULL;
    pj_pool_release(pool);
    return PJ_SUCCESS;
}

static unsigned ohos_audo_dev_get_dev_count(pjmedia_aud_dev_factory *ff)
{
    struct ohos_aud_factory *f = (struct ohos_aud_factory*)ff;
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_get_dev_count:%d",f->dev_count));
    return f->dev_count;
}

static pj_status_t ohos_audo_dev_get_dev_info(pjmedia_aud_dev_factory *ff,
                                        unsigned index,
                                        pjmedia_aud_dev_info *avinfo)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_get_dev_info index:%d",index));
    struct ohos_aud_factory *f = (struct ohos_aud_factory*)ff;
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_get_dev_info dev_count:%d",f->dev_count));
    PJ_ASSERT_RETURN(index < f->dev_count, PJMEDIA_EAUD_INVDEV);
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_get_dev_info samples_per:%d",f->dev_info[index].info.default_samples_per_sec));
    pj_memcpy(avinfo, &f->dev_info[index].info, sizeof(*avinfo));
    return PJ_SUCCESS;
}

static pj_status_t ohos_audo_dev_default_param(pjmedia_aud_dev_factory *ff,
                                         unsigned index,
                                         pjmedia_aud_param *param)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio ohos_audo_dev_default_param"));
    struct ohos_aud_factory *f = (struct ohos_aud_factory*)ff;
    pjmedia_aud_dev_info adi;
    pj_status_t status;

    PJ_ASSERT_RETURN(index < f->dev_count, PJMEDIA_EAUD_INVDEV);

    status = ohos_audo_dev_get_dev_info(ff, index, &adi);
    if (status != PJ_SUCCESS)
        return status;

    pj_bzero(param, sizeof(*param));
    PJ_LOG(4, (THIS_FILE, "avinfo->default_samples_per_sec:%d",adi.default_samples_per_sec));
    PJ_LOG(4, (THIS_FILE, "avinfo->index:%d",index));
    PJ_LOG(4, (THIS_FILE, "avinfo->input_count:%d",adi.input_count));
    PJ_LOG(4, (THIS_FILE, "avinfo->output_count:%d",adi.output_count));
    if (adi.input_count && adi.output_count) {
        param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
        param->rec_id = index;
        param->play_id = index;
        param->channel_count = adi.output_count;
    } else if (adi.input_count) {
        param->dir = PJMEDIA_DIR_CAPTURE;
        param->rec_id = index;
        param->play_id = PJMEDIA_AUD_INVALID_DEV;
        param->channel_count = adi.input_count;
    } else if (adi.output_count) {
        param->dir = PJMEDIA_DIR_PLAYBACK;
        param->play_id = index;
        param->rec_id = PJMEDIA_AUD_INVALID_DEV;
        param->channel_count = adi.output_count;
    } else {
        return PJMEDIA_EAUD_INVDEV;
    }

    param->clock_rate = adi.default_samples_per_sec; 
    param->samples_per_frame = adi.default_samples_per_sec * 20 / 1000;
     PJ_LOG(4, (THIS_FILE, "param->channel_count:%d",param->channel_count));
     PJ_LOG(4, (THIS_FILE, "param->default_samples_per_sec:%d",adi.default_samples_per_sec));          
     PJ_LOG(4, (THIS_FILE, "param->samples_per_frame:%d",param->samples_per_frame));                   
    // param->channel_count = 1;
    param->bits_per_sample = 16;
    param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
    param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;

    return PJ_SUCCESS;
}

static pj_status_t ohos_audo_dev_create_stream(pjmedia_aud_dev_factory *f,
                                         const pjmedia_aud_param *param,
                                         pjmedia_aud_rec_cb rec_cb,
                                         pjmedia_aud_play_cb play_cb,
                                         void *user_data,
                                         pjmedia_aud_stream **p_aud_strm)
{
    PJ_LOG(4, (THIS_FILE, "Creating Ohos stream start"));
    struct ohos_aud_factory *pa = (struct ohos_aud_factory*)f;
    pj_pool_t *pool;
    struct ohos_aud_stream *stream;
    pj_status_t status = PJ_SUCCESS;

    PJ_ASSERT_RETURN(param->channel_count >= 1 && param->channel_count <= 2,
                     PJ_EINVAL);
    PJ_ASSERT_RETURN(param->bits_per_sample==8 || param->bits_per_sample==16,
                     PJ_EINVAL);
    PJ_ASSERT_RETURN(play_cb && rec_cb && p_aud_strm, PJ_EINVAL);

    pool = pj_pool_create(pa->pf, "ohosstrm", 1024, 1024, NULL);
    if (!pool)
        return PJ_ENOMEM;

    PJ_LOG(4, (THIS_FILE, "Creating Ohos stream"));
    stream = PJ_POOL_ZALLOC_T(pool, struct ohos_aud_stream);
    stream->pool = pool;
    stream->f = pa;
    pj_strdup2_with_null(pool, &stream->name, "Ohos stream");
    stream->dir = param->dir;
    pj_memcpy(&stream->param, param, sizeof(*param));
    stream->user_data = user_data;
    stream->rec_cb = rec_cb;
    stream->play_cb = play_cb;
    
    PJ_LOG(4,(THIS_FILE, "ohos stream clock_rate=%d , channel_count=%d",  param->clock_rate, param->channel_count));
    if (param->dir & PJMEDIA_DIR_CAPTURE) {
        PJ_LOG(4, (THIS_FILE, "Creating Ohos stream PJMEDIA_DIR_CAPTURE"));
        stream->rec_engine = new AudioCapturer(stream, param->clock_rate, param->channel_count);
        if (!stream->rec_engine) {
            PJ_LOG(3, (THIS_FILE, "Creating AudioCapturer error"));
            status = PJ_ENOMEM;
            goto on_error;
        }
    }
//stream->play_engine = new AudioRenderer(stream, stream->param.clock_rate, stream->param.channel_count);
    if (stream->dir & PJMEDIA_DIR_PLAYBACK) {
        PJ_LOG(4, (THIS_FILE, "Creating Ohos stream PJMEDIA_DIR_PLAYBACK"));
        stream->play_engine = new AudioRenderer(stream, param->clock_rate, param->channel_count,param->samples_per_frame *param->bits_per_sample / 8);
        if (!stream->play_engine) {
            PJ_LOG(3, (THIS_FILE, "Creating AudioRenderer error"));
            status = PJ_ENOMEM;
            goto on_error;
        }
    }
    PJ_LOG(4, (THIS_FILE, "Creating Ohos stream done"));
    /* Done */
    stream->base.op = &ohos_audo_dev_strm_op;
    *p_aud_strm = &stream->base;
    PJ_LOG(4, (THIS_FILE, "Creating Ohos stream success"));
    return PJ_SUCCESS;
on_error:
    PJ_LOG(4, (THIS_FILE, "Creating Ohos stream on_error"));
    strm_destroy(&stream->base);
    return status;
}                                         

static pj_status_t strm_get_param(pjmedia_aud_stream *s,
                                  pjmedia_aud_param *pi)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio strm_get_param"));
    struct ohos_aud_stream *strm = (struct ohos_aud_stream*)s;
    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
    pj_memcpy(pi, &strm->param, sizeof(*pi));

    return PJ_SUCCESS;
}

static pj_status_t strm_get_cap(pjmedia_aud_stream *s,
                                pjmedia_aud_dev_cap cap,
                                void *value)
{
        PJ_LOG(4, (THIS_FILE, "ohos_avdio strm_get_cap"));
    struct ohos_aud_stream *strm = (struct ohos_aud_stream*)s;
    pj_status_t status = PJMEDIA_EAUD_INVCAP;

    PJ_UNUSED_ARG(strm);

    PJ_ASSERT_RETURN(s && value, PJ_EINVAL);
    return status;
}

static pj_status_t strm_set_cap(pjmedia_aud_stream *s,
                                pjmedia_aud_dev_cap cap,
                                const void *value)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio strm_set_cap"));
    struct ohos_aud_stream *strm = (struct ohos_aud_stream*)s;

    PJ_UNUSED_ARG(strm);

    PJ_ASSERT_RETURN(s && value, PJ_EINVAL);

    return PJMEDIA_EAUD_INVCAP;
}

static pj_status_t strm_start(pjmedia_aud_stream *s)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio strm_start"));    
    struct ohos_aud_stream *stream = (struct ohos_aud_stream*)s;
    pj_status_t status;
    if (stream->running)
        return PJ_SUCCESS;

    if (stream->rec_engine) {
        status = stream->rec_engine->start();
        if (status != PJ_SUCCESS)
            goto on_error;
    }
    if (stream->play_engine) {
        status = stream->play_engine->start();
        if (status != PJ_SUCCESS)
            goto on_error;
    }

    stream->running = PJ_TRUE;
    PJ_LOG(4, (THIS_FILE, "ohos audio stream started"));

    return PJ_SUCCESS;

on_error:
    if (stream->rec_engine)
        stream->rec_engine->stop();
    if (stream->play_engine)
        stream->play_engine->stop();

    PJ_LOG(4, (THIS_FILE, "Failed starting ohos audio stream"));
    return status;    
}

static pj_status_t strm_stop(pjmedia_aud_stream *s)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio strm_stop"));    
    struct ohos_aud_stream *stream = (struct ohos_aud_stream*)s;

    if (!stream->running)
        return PJ_SUCCESS;

    stream->running = PJ_FALSE;

    if (stream->rec_engine)
        stream->rec_engine->stop();
    if (stream->play_engine)
        stream->play_engine->stop();

    PJ_LOG(4,(THIS_FILE, "ohos audio stream stopped"));

    return PJ_SUCCESS;
}

static pj_status_t strm_destroy(pjmedia_aud_stream *s)
{
    PJ_LOG(4, (THIS_FILE, "ohos_avdio strm_destroy"));    
    struct ohos_aud_stream *stream = (struct ohos_aud_stream*)s;

    PJ_LOG(4,(THIS_FILE, "Destroying ohos audio stream..."));

    strm_stop(s);

    if (stream->rec_engine) {
        stream->rec_engine->destroy();
        delete stream->rec_engine;
        stream->rec_engine = NULL;
    }
    if (stream->play_engine) {
        stream->play_engine->destroy();
        delete stream->play_engine;
        stream->play_engine = NULL;
    }

    pj_pool_release(stream->pool);
    PJ_LOG(4, (THIS_FILE, "ohos audio stream destroyed"));
    return PJ_SUCCESS;
}
#endif  /* PJMEDIA_AUDIO_DEV_HAS_OHOS */